using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.WebControls;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint;

namespace SharePointLikeFeature
{
    [Guid("8bd99406-6fa6-46ae-8049-cd618e915b40")]
    public class LikeButtonWebPart : Microsoft.SharePoint.WebPartPages.WebPart
    {
        private bool _error = false;
        private string _likeButtonText = null;
        private string _unLikeButtonText = null;
        private string _likeListName = null;
        private string _likeFieldName = null;
        private string _commentsButtonText = null;
        private string _commentsListName = null;
        private string listItemTemplate = "<li class=\"CommentItem\"><span class=\"CommentBody\">{1}</span><span class=\"CommentAuthor\"> by {2}</span></li>";

        long likeCount = 0;
        private Button likeButton;
        private Button commentButton;
        private TextBox commentsText;
        private LiteralControl likeText;
        private LiteralControl commentsListControl;

        [Personalizable(PersonalizationScope.Shared)]
        [WebBrowsable(true)]
        [System.ComponentModel.Category("Like Button")]
        [WebDisplayName("Like Button Text")]
        [WebDescription("Text to be used on the Like button")]
        public string LikeButtonText
        {
            get
            {
                if (_likeButtonText == null)
                {
                    _likeButtonText = "I like this";
                }
                return _likeButtonText;
            }
            set { _likeButtonText = value; }
        }


        [Personalizable(PersonalizationScope.Shared)]
        [WebBrowsable(true)]
        [System.ComponentModel.Category("Like Button")]
        [WebDisplayName("UnLike Button Text")]
        [WebDescription("Text to be used on the UnLike button")]
        public string UnLikeButtonText
        {
            get
            {
                if (_unLikeButtonText == null)
                {
                    _unLikeButtonText = "Unlike this";
                }
                return _unLikeButtonText;
            }
            set { _unLikeButtonText = value; }
        }


        [Personalizable(PersonalizationScope.Shared)]
        [WebBrowsable(true)]
        [System.ComponentModel.Category("Like Button")]
        [WebDisplayName("Like Button List Name")]
        [WebDescription("Name of the list used to store who likes this item")]
        public string LikeListName
        {
            get
            {
                if (_likeListName == null)
                {
                    _likeListName = "Like List";
                }
                return _likeListName;
            }
            set { _likeListName = value; }
        }


        [Personalizable(PersonalizationScope.Shared)]
        [WebBrowsable(true)]
        [System.ComponentModel.Category("Like Button")]
        [WebDisplayName("Like Button Field Name")]
        [WebDescription("Name of the field in this list that should contain the count of Likes")]
        public string LikeFieldName
        {
            get
            {
                if (_likeFieldName == null)
                {
                    _likeFieldName = "Likes";
                }
                return _likeFieldName;
            }
            set { _likeFieldName = value; }
        }


        [Personalizable(PersonalizationScope.Shared)]
        [WebBrowsable(true)]
        [System.ComponentModel.Category("Comments")]
        [WebDisplayName("Comments Button text")]
        [WebDescription("Text to be used on the Post Comment button")]
        public string CommentsButtonText
        {
            get
            {
                if (_commentsButtonText == null)
                {
                    _commentsButtonText = "Post Comment";
                }
                return _commentsButtonText;
            }
            set { _commentsButtonText = value; }
        }

        [Personalizable(PersonalizationScope.Shared)]
        [WebBrowsable(true)]
        [System.ComponentModel.Category("Comments")]
        [WebDisplayName("Comments List Name")]
        [WebDescription("Name of the list used to store the comments made against this item")]
        public string CommentsListName
        {
            get
            {
                if (_commentsListName == null)
                {
                    _commentsListName = "Comments List";
                }
                return _commentsListName;
            }
            set { _commentsListName = value; }
        }





        public LikeButtonWebPart()
        {
            this.ExportMode = WebPartExportMode.All;
        }

        /// <summary>
        /// Create all your controls here for rendering.
        /// Try to avoid using the RenderWebPart() method.
        /// </summary>
        protected override void CreateChildControls()
        {
            if (!_error)
            {
                try
                {

                    base.CreateChildControls();

                    // Check for existing Like record...

                    SPWeb thisWeb = SPContext.Current.Web;
                    SPList thisList = SPContext.Current.List;
                    SPItem thisItem = SPContext.Current.Item;
                    SPUser thisUser = thisWeb.CurrentUser;
                    
                    bool alreadyLiked = false;
                    SPListItemCollection existingComments = null;

                    try
                    {
                        
                        // Get a reference to the Like List
                        SPList likeList = thisWeb.Lists[this.LikeListName];
                        if (likeList != null)
                        {
                            
                            // Perform query against the Like List to determine if this Item is liked
                            SPQuery alreadyLikeQuery = new SPQuery();
                            alreadyLikeQuery.Query = "<Where>"
                                + "<And>"
                                    + "<Eq><FieldRef Name='Title'/><Value Type='Text'>" + thisList.ID + "</Value></Eq>"
                                    + "<And>"
                                        + "<Eq><FieldRef Name='Item_x0020_ID'/><Value Type='Number'>" + thisItem.ID + "</Value></Eq>"
                                        + "<Eq><FieldRef Name='Author'/><Value Type='User'>" + thisUser.Name + "</Value></Eq>"
                                    + "</And>"
                                + "</And>"
                                + "</Where>";

                            SPListItemCollection likedItems = likeList.GetItems(alreadyLikeQuery);
                            if (likedItems.Count > 0) alreadyLiked = true;

                            // Determine if the Like Field is in this Item's field collection
                            if (thisItem.Fields.ContainsField(this.LikeFieldName))
                            {

                                // If there's a Like Field in this Item's field collection, determine the numeric value of how many users have liked the Item from there
                                SPField thisField = thisItem.Fields.GetField(this.LikeFieldName);
                                try
                                {
                                    likeCount = long.Parse(thisItem[thisField.InternalName].ToString());
                                }
                                catch (Exception ex)
                                {
                                }
                            }
                            else
                            {

                                // If there's no Like Field in this Item's field collection, query the Like List to determine how many users have liked the Item
                                SPQuery likeCountQuery = new SPQuery();
                                likeCountQuery.Query = "<Where>"
                                     + "<And>"
                                         + "<Eq><FieldRef Name='Title'/><Value Type='Text'>" + thisList.ID + "</Value></Eq>"
                                         + "<Eq><FieldRef Name='Item_x0020_ID'/><Value Type='Number'>" + thisItem.ID + "</Value></Eq>"
                                     + "</And>"
                                     + "</Where>";
                                likedItems = likeList.GetItems(likeCountQuery);
                                likeCount = likedItems.Count;
                            }

                        }
                    }
                    catch (Exception ex)
                    { }

                    try
                    {
                        
                        // Get a reference to the Comments List
                        SPList commentsList = thisWeb.Lists[this.CommentsListName];
                        if (commentsList != null)
                        {
                            
                            // Perform a query to pull back all the Comments made against this Item
                            SPQuery commentsQuery = new SPQuery();
                            commentsQuery.Query = "<Where>"
                                + "<And>"
                                    + "<Eq><FieldRef Name='Title'/><Value Type='Text'>" + thisList.ID + "</Value></Eq>"
                                    + "<Eq><FieldRef Name='Item_x0020_ID'/><Value Type='Number'>" + thisItem.ID + "</Value></Eq>"
                                + "</And>"
                                + "</Where>"
                                + "<OrderBy>"
                                    + "<FieldRef Name='Sequence' Ascending='FALSE' />"
                                +"</OrderBy>";
                            

                            existingComments = commentsList.GetItems(commentsQuery);

                        }

                    }
                    catch (Exception ex)
                    { }

                    // Add a page header reference to the SharePointLikeFeature's stylesheet
                    this.Page.Header.Controls.Add(new LiteralControl("<link rel=\"stylesheet\" type=\"text/css\" href=\"/_layouts/SharePointLikeFeature/SharePointLikeFeature.css\"/>"));
                    
                    // Start rendering the Like Button box
                    this.Controls.Add(new LiteralControl("<div class=\"LikeButtonBox\">"));
                    
                    // Render the text of how many people already Like this Item
                    likeText = new LiteralControl();
                    if (likeCount == 0) likeText.Text = "No one likes this yet.<br/>";
                    if (likeCount == 1) likeText.Text = likeCount + " person likes this already.<br/>";
                    if (likeCount > 1) likeText.Text = likeCount + " people like this already.<br/>";
                    this.Controls.Add(likeText);
                    
                    
                    // Render the button for the current user to Like or UnLike this Item
                    likeButton = new Button();
                    likeButton.ID = "btnLikeIt";

                    if (!alreadyLiked)
                    {
                        likeButton.Text = this.LikeButtonText;
                        likeButton.Click += new EventHandler(likeButton_Click);
                        likeButton.CssClass = "ILikeItButtonToLike";
                    }
                    else
                    {
                        likeButton.Text = this.UnLikeButtonText;
                        likeButton.Click += new EventHandler(unLikeButton_Click);
                        likeButton.CssClass = "ILikeItButtonAlready";
                    }

                    this.Controls.Add(likeButton);

                    // Close the Like Button box
                    this.Controls.Add(new LiteralControl("</div>"));

                    // Start rendering the Comments box
                    this.Controls.Add(new LiteralControl("<div class=\"CommentsBox\">"));
                    this.Controls.Add(new LiteralControl("<ul id=\"CommentsContainer\">"));

                    // Start rendering the list of comments from the previous Comments query
                    commentsListControl = new LiteralControl();
                    foreach (SPListItem thisComment in existingComments)
                    {
                        string thisCommentText = "";
                        if (thisComment["Comment"] != null) thisCommentText = thisComment["Comment"].ToString();
                        commentsListControl.Text += listItemTemplate.Replace("{1}", thisCommentText).Replace("{2}", thisComment["Author"].ToString().Substring(thisComment["Author"].ToString().IndexOf(";#") + 2));
                    }
                    this.Controls.Add(commentsListControl);
                    this.Controls.Add(new LiteralControl("</ul>"));

                    // Render a text box for the user to enter their own comment
                    commentsText = new TextBox();
                    commentsText.TextMode = TextBoxMode.MultiLine;
                    commentsText.ID = "txtComments";
                    commentsText.CssClass = "CommentsTextBox";
                    this.Controls.Add(commentsText);

                    // Render a button for the user to submit their own comment
                    commentButton = new Button();
                    commentButton.Text = this.CommentsButtonText;
                    commentButton.ID = "btnMakeComment";
                    commentButton.Click += new EventHandler(commentButton_Click);
                    this.Controls.Add(commentButton);

                    // Close the Comments box
                    this.Controls.Add(new LiteralControl("</div>"));

                }
                catch (Exception ex)
                {
                    HandleException(ex);
                }
            }
        }

        void commentButton_Click(object sender, EventArgs e)
        {
            // Look for the Web, List and Item on which this Comment has been made
            SPWeb thisWeb = SPContext.Current.Web;
            SPList thisList = SPContext.Current.List;
            SPItem thisItem = SPContext.Current.Item;

            try
            {
                
                // Get a reference to the Comments List
                SPList commentList = thisWeb.Lists[this.CommentsListName];

                // Check if the Comments List exists, and that the Comment contains some text
                if (commentList != null && commentsText.Text != "")
                {
                    
                    // Create a new Item in the Comments List
                    SPListItem commentRecord = commentList.Items.Add();

                    commentRecord["List GUID"] = thisList.ID.ToString();
                    commentRecord["Item ID"] = thisItem.ID.ToString();
                    commentRecord["List Reference"] = thisList.DefaultViewUrl + ", " + thisList.Title;
                    commentRecord["Item Reference"] = thisList.ParentWeb.Url + "/" + thisList.Forms[PAGETYPE.PAGE_DISPLAYFORM].Url + "?id=" + thisItem.ID.ToString() + ", " + thisItem["Title"];
                    commentRecord["Comment"] = commentsText.Text;
                    commentRecord.Update();

                    // Check if the new Item in the Comments List has been committed to the database
                    if (commentRecord.ID > 0)
                    {

                        // Add the Comment to the list of existing Comments against this Item
                        commentsListControl.Text += listItemTemplate.Replace("{1}", commentsText.Text).Replace("{2}", SPContext.Current.Web.CurrentUser.Name);
                        
                        // Clear out the Comments text box ready for the next Comment
                        commentsText.Text = "";
                    }
                }
            }
            catch (Exception ex)
            {
                throw new SPException("The following error occurred when processing the Like request: [" + ex.Message + "]");
            }

        }

        void likeButton_Click(object sender, EventArgs e)
        {
            // Look for the Web, List and Item on which this Like button has been added
            SPWeb thisWeb = SPContext.Current.Web;
            SPList thisList = SPContext.Current.List;
            SPItem thisItem = SPContext.Current.Item;

            bool changeButtonFace = false;

            try
            {
                
                // Get a reference to the Like List
                SPList likeList = thisWeb.Lists[this.LikeListName];
                
                // Check if the Like List includes fields of the recent upgrade to the SharePointFLikeFeature, and create them if required
                if (!likeList.Fields.ContainsField("List Reference")) likeList.Fields.Add("List Reference", SPFieldType.URL, false);
                if (!likeList.Fields.ContainsField("Item Reference")) likeList.Fields.Add("Item Reference", SPFieldType.URL, false);

            }
            catch (Exception ex)
            {
                throw new SPException("The following error occurred when trying to upgrade the Like List [" + this.LikeListName + "] with the template fields: [" + ex.Message + "]");
            }

            try
            {

                // Get a reference to the Like List
                SPList likeList = thisWeb.Lists[this.LikeListName];
    
                // Since the Item is being Liked, indicate as such for later when the Like button is rendered
                changeButtonFace = true;
                if (likeList != null)
                {

                    // If the Like List exists, add a record to indicate this user Likes this Item
                    SPListItem likeRecord = likeList.Items.Add();

                    likeRecord["List GUID"] = thisList.ID.ToString();
                    likeRecord["Item ID"] = thisItem.ID.ToString();
                    likeRecord["List Reference"] = thisList.DefaultViewUrl + ", " + thisList.Title;
                    likeRecord["Item Reference"] = thisList.ParentWeb.Url + "/" + thisList.Forms[PAGETYPE.PAGE_DISPLAYFORM].Url + "?id=" + thisItem.ID.ToString() + ", " + thisItem["Title"];
                    likeRecord.Update();
                }
            }
            catch (Exception ex)
            {
                throw new SPException("The following error occurred when processing the Like request: [" + ex.Message + "]");
            }

            // Check if the Item contains a Like Field
            if (thisItem.Fields.ContainsField(this.LikeFieldName))
            {
                
                // If the Item does contain a Like Field, incremement the number contained in that field by one
                long updateLikeCount = 0;

                SPField thisField = thisItem.Fields.GetField(this.LikeFieldName);
                try
                {
                    updateLikeCount = long.Parse(thisItem[thisField.InternalName].ToString());
                }
                catch (Exception ex)
                {
                }
                updateLikeCount++;
                thisItem[thisField.InternalName] = updateLikeCount.ToString().Trim();
                thisItem.Update();
            }

            if (likeButton != null && changeButtonFace)
            {
                
                // Change the Like button to use the UnLike parameters
                likeButton.Text = this.UnLikeButtonText;
                likeButton.Click -= likeButton_Click;
                likeButton.Click += unLikeButton_Click;
            }

            // Change the Like count text to refer to the new number of likes for this Item
            likeCount++;
            if (likeCount == 0) likeText.Text = "No one likes this yet. <br/>";
            if (likeCount == 1) likeText.Text = likeCount + " person likes this already.<br/>";
            if (likeCount > 1) likeText.Text = likeCount + " people like this already.<br/>";

        }

        void unLikeButton_Click(object sender, EventArgs e)
        {

            // Look for the Web, List, Item and Current User
            SPWeb thisWeb = SPContext.Current.Web;
            SPList thisList = SPContext.Current.List;
            SPItem thisItem = SPContext.Current.Item;
            SPUser thisUser = thisWeb.CurrentUser;


            try
            {
                
                // Get a reference to the Like List
                SPList likeList = thisWeb.Lists[this.LikeListName];
                if (likeList != null)
                {
                    
                    // Query the Like list for all records that refer to this Item and User
                    SPQuery alreadyLikeQuery = new SPQuery();
                    alreadyLikeQuery.Query = "<Where>"
                        + "<And>"
                            + "<Eq><FieldRef Name='Title'/><Value Type='Text'>" + thisList.ID + "</Value></Eq>"
                            + "<And>"
                                + "<Eq><FieldRef Name='Item_x0020_ID'/><Value Type='Number'>" + thisItem.ID + "</Value></Eq>"
                                + "<Eq><FieldRef Name='Author'/><Value Type='User'>" + thisUser.Name + "</Value></Eq>"
                            + "</And>"
                        + "</And>"
                        + "</Where>";

                    
                    // Start deleting all Items found by the above query
                    SPListItemCollection likedItems = likeList.GetItems(alreadyLikeQuery);
                    long likedItemsCount = likedItems.Count;

                    for (int x = 0; x < likedItemsCount; x++)
                    {
                        likedItems.Delete(0);
                    }

                }
            }
            catch (Exception ex)
            { }

            
            // Check if this Item contains a Like Field
            if (thisItem.Fields.ContainsField(this.LikeFieldName))
            {
                long updateLikeCount = 1;

                // Decrement the Like Count contained in the Like Field
                SPField thisField = thisItem.Fields.GetField(this.LikeFieldName);
                try
                {
                    updateLikeCount = long.Parse(thisItem[thisField.InternalName].ToString());
                }
                catch (Exception ex)
                {
                }
                updateLikeCount--;
                thisItem[thisField.InternalName] = updateLikeCount.ToString().Trim();
                thisItem.Update();
            }

            if (likeButton != null)
            {

                // Update the Like Button text to use the Like parameters
                likeButton.Text = this.LikeButtonText;
                likeButton.Click -= unLikeButton_Click;
                likeButton.Click += likeButton_Click;
            }

            // Change the Like count text to refer to the new number of likes for this Item
            likeCount--;
            if (likeCount == 0) likeText.Text = "No one likes this yet.<br/>";
            if (likeCount == 1) likeText.Text = likeCount + " person likes this already.<br/>";
            if (likeCount > 1) likeText.Text = likeCount + " people like this already.<br/>";

        }
        

        /// <summary>
        /// Ensures that the CreateChildControls() is called before events.
        /// Use CreateChildControls() to create your controls.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLoad(EventArgs e)
        {
            if (!_error)
            {
                try
                {
                    base.OnLoad(e);
                    this.EnsureChildControls();

                    // Your code here...
                }
                catch (Exception ex)
                {
                    HandleException(ex);
                }
            }
        }

        /// <summary>
        /// Clear all child controls and add an error message for display.
        /// </summary>
        /// <param name="ex"></param>
        private void HandleException(Exception ex)
        {
            this._error = true;
            this.Controls.Clear();
            this.Controls.Add(new LiteralControl(ex.Message));
        }
    }
}
